package hy.iot.core.support;

import cn.hutool.core.exceptions.ExceptionUtil;
import hy.iot.core.annotation.IotField;
import hy.iot.core.annotation.IotFieldClass;
import hy.iot.core.entity.IMessage;
import hy.iot.core.entity.parseTypeEnum;
import hy.iot.core.exception.FieldFailCodes;
import hy.iot.core.exception.IotFieldException;
import hy.iot.core.handler.Handler;
import hy.iot.core.utils.FieldsUtils;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 扫描支持
 *
 * 扫描并加载所有驱动文件
 *
 *
 * @author lcc
 */
@Slf4j
@Component
public class IotFieldTranseSupport {

    private IotFieldConfig config;

    private Boolean initStatus = false;

    private Map<String, Handler> schema;

    private Map<String, Handler> endpoint;

    private Map<String, Class> fidldSchemaMap;
    private Class<?> headerClass;

    private final String defaultSchema = "hy.iot.core.schema";

    public IotFieldTranseSupport(IotFieldConfig config) {
        this.config = config;
        this.headerClass = config.getHeaderClass();
        this.initStatus = false;
        this.schema = new HashMap<>();
        this.endpoint = new HashMap<>();
        this.fidldSchemaMap = new HashMap<>();
        init();
    }

    private void init(){
        verify();
        // 加载协议解析器
        ParserSupport ps = new ParserSupport(defaultSchema,config.getParserPackagesPath());
        this.schema = ps.getSchemas();
        // 加载功能文件类
        FieldScanClass fc = new FieldScanClass(config.getDeviceProtocolClassPackagesPath(),config.getHeaderClass());
        this.fidldSchemaMap = fc.getFidldSchemaMap();
        // 加载响应接口
        EndpointScanSupport ep = new EndpointScanSupport(config.getEndpointsPackagesPath());
        this.endpoint = ep.getEndpointMap();
        this.initStatus = true;
    }

    private void verify(){
        if(this.config == null || StringUtils.isBlank(this.config.getDeviceProtocolClassPackagesPath())
                || this.config.getHeaderClass() == null
                || StringUtils.isBlank(this.config.getEndpointsPackagesPath())
        ){
            throw new IotFieldException(FieldFailCodes.IOT_CONFIG_FAIL);
        }
    }

    /**
     * 协议解析
     *  先根据头文件 解析出功能码 获取子类 再次解析
     *  调用 响应函数
     * @param hexString
     * @return
     */
    public Boolean mergeFrom(String hexString,ChannelHandlerContext ctx){
        if (!this.initStatus){
            throw new IotFieldException(FieldFailCodes.IOT_FAIL_INIT);
        }
        try {
            Object hc =  headerClass.newInstance();
            // 先对头部文件解析
            setFields(hc,hc,hexString,"");
            IMessage iMessage = (IMessage) hc;
            // 解析全对象
//            Object cp = fidldSchemaMap.get(iMessage.getPointType()).newInstance();
            Class ca = fidldSchemaMap.get(iMessage.getPointType());
            if (ca != null){
                Object cp = ca.newInstance();
                setFields(hc,cp,hexString,iMessage.getVersion());
                setFields(cp,cp,hexString,iMessage.getVersion());
                Handler res = endpoint.get(iMessage.getPointType());
                //调用响应接口
                res.invoke(cp,ctx);
                return true;
            }
            log.warn("未解析{}设备点位:{} 协议解析版本号: {} source data: {}",config.getDeviceType(),iMessage.getPointType(),iMessage.getVersion(),hexString);
        } catch (Exception e) {
            throw new IotFieldException(FieldFailCodes.IOT_SCHEMA_PARSE_FAIL, ExceptionUtil.stacktraceToOneLineString(e));
        }
        return false;
    }

    /**
     * TODO 增加版本号匹配解析
     *
     * @param df
     * @param o
     * @param hexStr
     * @param version
     */
    private void setFields(Object df, Object o, String hexStr, String version) {
        Field[] fields = df.getClass().getDeclaredFields();
        IotFieldClass _class = df.getClass().getAnnotation(IotFieldClass.class);
        String splitMark = null;
        String[] data = null;
        if (_class != null) {
            // 判断 父节点是否分割
            if (StringUtils.isNotBlank(_class.parseSchema())){
                hexStr = getParserObject(_class.parseSchema(),hexStr).toString();
            }
            splitMark = _class.hexValueOptions();
            if (StringUtils.isNotBlank(splitMark)){
                data = hexStr.split(splitMark);
            }
        }
        int startIndex = 0;
        for (Field field : fields) {
            IotField _fieldHex = (IotField) field.getAnnotation(IotField.class);
            try {
                if (!Arrays.stream(_fieldHex.version()).filter(s->s.equals(StringUtils.isBlank(version)?"0":version)).findFirst().isPresent()){
                    continue;
                }
                if (_fieldHex.hexLength() < 0 || _fieldHex.hexStart() < 0 || StringUtils.isBlank(_fieldHex.parseSchema())) {
                    continue;
                }
                // 使用哪种解析器 hexString 截取长度
                String parseType = _fieldHex.parseSchema();
                String hext = "";
                Object result;
                if (StringUtils.isNotBlank(splitMark)) {
                    // 从 data 数组中匹配数据 配置父类 手表 特殊处理
                    hext = FieldsUtils.getHexStrData(data,_fieldHex,startIndex);
                    result = getParserObject(parseType,hext);
                    if (_fieldHex.parseType().equals(parseTypeEnum.LIST)){
                        // 如果当前数据是可变长度 则 将当前数据 变更为长度 重新计算
                        // hext = 当前起始位 , 当前起始位 + 长度 + (可变长度 * 缩放)
                        List listHext = FieldsUtils.getZoomData(result,data,_fieldHex,startIndex);
                        Integer dataLen = Integer.valueOf(result.toString());
                        Integer _sl = _fieldHex.hexStart() + _fieldHex.hexLength();
                        startIndex = _sl + (dataLen * _fieldHex.zoomNum())+ _fieldHex.afterZoomNum();
                        result = listHext;
                    }else{
                        startIndex = _fieldHex.hexStart()+_fieldHex.hexLength()+1;
                    }
                }else{
                    // 从 hexStr 中匹配数据 父类未配置
                    hext = FieldsUtils.getHexStr(hexStr,_fieldHex);
                    result = getParserObject(parseType,hext);
                    if (_fieldHex.parseType().equals(parseTypeEnum.LIST)){
                        // 如果当前数据是可变长度则将当前数据 变更为长度 重新计算
                        // hext = 当前起始位 , 当前起始位 + 长度 + (可变长度 * 缩放)
                        List listHext = FieldsUtils.getZoomData(result,data,_fieldHex, startIndex);
                        Integer dataLen = Integer.valueOf(result.toString());
                        Integer _sl = _fieldHex.hexStart() + _fieldHex.hexLength();
                        startIndex = _sl + (dataLen * _fieldHex.zoomNum()) + _fieldHex.afterZoomNum() +1;
                        result = listHext;
                    }else{
                        startIndex = _fieldHex.hexStart()+_fieldHex.hexLength()+1;
                    }
                }
                FieldsUtils.SetField(field,o,hext,result,_fieldHex.desc());
            } catch (Exception ex) {
                log.info(" {} parsing  resource msg :{} error {}",_fieldHex.desc(),hexStr,ExceptionUtil.stacktraceToString(ex,Integer.MAX_VALUE));
                throw new IotFieldException(FieldFailCodes.IOT_SCHEMA_PARSE_FAIL);
            }
        }
    }

    private Object getParserObject(String pt, String hexStr) {
        Handler handler = schema.get(pt);
        if (handler == null) {
            throw new IotFieldException(FieldFailCodes.IOT_SCHEMA_UN_KNOWN);
        }
        try {
            return handler.invoke(hexStr);
        } catch (Exception e) {
            throw new IotFieldException(FieldFailCodes.IOT_SCHEMA_PARSE_FAIL);
        }
    }


}
